/**
 * WTF Engine
 *
 * License... etc.
 **
 * Render Module
 *
 * Provides:
 *	- Modelview matrix transforms
 *	- render routine
 *  - bindings to important OpenGL calls
 **
 * Authors: Sean Micklethwaite
 **/


#include "render.h"
#include "glext.h"
#include "sdlmisc.h"
#include "graphics_functions.h"

#if !defined(NDEBUG) && defined(__PHYSICS_BULLET__)
//#	define _RENDER_PHYSICS_DEBUG
//#	define _RENDER_NO_OBJECTS
#	include "physics.h"
#endif

using namespace WtfEngine;

PFNGLBINDRENDERBUFFEREXTPROC		glBindRenderbufferEXT;
PFNGLRENDERBUFFERSTORAGEEXTPROC		glRenderbufferStorageEXT;
PFNGLBINDFRAMEBUFFEREXTPROC			glBindFramebufferEXT;
PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC	glCheckFramebufferStatusEXT;
PFNGLFRAMEBUFFERTEXTURE2DEXTPROC	glFramebufferTexture2DEXT;
PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbufferEXT;
PFNGLGENERATEMIPMAPEXTPROC			glGenerateMipmapEXT;
PFNGLGENRENDERBUFFERSEXTPROC		glGenRenderbuffersEXT;
PFNGLGENFRAMEBUFFERSEXTPROC			glGenFramebuffersEXT;
PFNGLDELETERENDERBUFFERSEXTPROC		glDeleteRenderbuffersEXT;
PFNGLDELETEFRAMEBUFFERSEXTPROC		glDeleteFramebuffersEXT;

// VBO Related
PFNGLGENBUFFERSARBPROC				glGenBuffersARB;
PFNGLBINDBUFFERARBPROC				glBindBufferARB;
PFNGLBUFFERDATAARBPROC				glBufferDataARB;
PFNGLDELETEBUFFERSARBPROC			glDeleteBuffersARB;

// GLSL Related
PFNGLCREATESHADEROBJECTARBPROC		glCreateShaderObjectARB;
PFNGLSHADERSOURCEARBPROC			glShaderSourceARB;
PFNGLCOMPILESHADERARBPROC			glCompileShaderARB;
PFNGLGETOBJECTPARAMETERIVARBPROC	glGetObjectParameterivARB;
PFNGLGETINFOLOGARBPROC				glGetInfoLogARB;
PFNGLCREATEPROGRAMOBJECTARBPROC		glCreateProgramObjectARB;
PFNGLATTACHOBJECTARBPROC			glAttachObjectARB;
PFNGLLINKPROGRAMARBPROC				glLinkProgramARB;
PFNGLUSEPROGRAMOBJECTARBPROC		glUseProgramObjectARB;
PFNGLGETUNIFORMLOCATIONARBPROC		glGetUniformLocationARB;
PFNGLGETATTRIBLOCATIONARBPROC		glGetAttribLocationARB;
PFNGLUNIFORM2FARBPROC				glUniform2fARB;
PFNGLUNIFORM3FARBPROC				glUniform3fARB;
PFNGLUNIFORM1IARBPROC				glUniform1iARB;
PFNGLVERTEXATTRIBPOINTERARBPROC		glVertexAttribPointerARB;
PFNGLENABLEVERTEXATTRIBARRAYARBPROC	glEnableVertexAttribArrayARB;
PFNGLDISABLEVERTEXATTRIBARRAYARBPROC	glDisableVertexAttribArrayARB;

#ifndef __GNUC__
PFNGLACTIVETEXTUREARBPROC			glActiveTextureARB;
PFNGLCLIENTACTIVETEXTUREPROC		glClientActiveTexture;
#endif


Renderable::Renderable(const Renderable::tCreationOptions& args, const WtfGraphics::GraphicsData::tCacheRef& d)
	: mrGraphicsData(d), GameObject(args)
{
	Renderer::GetInstance().PushObject(this);
};

void Renderable::Destroy()
{
	Renderer::GetInstance().RemoveObject(this);
	this->GameObject::Destroy();
};


unsigned Renderable::CalculateSerializedSize() {
	// Need to serialize the graphics data reference.
	return GameObject::CalculateSerializedSize() + mrGraphicsData.CalculateSerializedSize();
};
unsigned Renderable::Serialize(void *pBuf) {
	unsigned n = GameObject::Serialize(pBuf);
	return n + mrGraphicsData.Serialize((void *)((unsigned)pBuf + n));
};
unsigned Renderable::Deserialize(void *pBuf) {
	unsigned n = GameObject::Deserialize(pBuf);
	return n + mrGraphicsData.Deserialize((void *)((unsigned)pBuf + n));
};



/**** The renderer ****/

Renderer::Renderer(WtfGraphics::tScreenRef& screen)
: Task<Renderer>(500, "Renderer"), Loggable<Renderer>("Renderer"), mRenderType(RENDER_WORLD)
{
	mpRoot = new Ogre::Root();

	// OGRE configuration
	if(!mpRoot->restoreConfig() && !mpRoot->showConfigDialog())
		throw std::runtime_error("Graphics engine not configured");

	mpRoot->initialise(false);
	Ogre::NameValuePairList opts;

#ifdef __WM_WIN32__
	opts["externalWindowHandle"] = Ogre::StringConverter::toString(screen->getWindowHandle());
	mpRenderWindow = mpRoot->createRenderWindow("WTF Engine",
		screen->getSize().x(), screen->getSize().y(), false, &opts);
#else

#endif
	Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

	mpScene = mpRoot->createSceneManager(Ogre::ST_GENERIC, "Renderer Scene");
	mpOgreCamera = mpScene->createCamera("Camera");
	mpViewport = mpRenderWindow->addViewport(mpOgreCamera);

	//
	// Load extension functions
	//

	glBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC)
		SDL_GL_GetProcAddress("glBindRenderbufferEXT");
	glRenderbufferStorageEXT = (PFNGLRENDERBUFFERSTORAGEEXTPROC)
		SDL_GL_GetProcAddress("glRenderbufferStorageEXT");
	glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC)
		SDL_GL_GetProcAddress("glBindFramebufferEXT");
	glCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)
		SDL_GL_GetProcAddress("glCheckFramebufferStatusEXT");
	glFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)
		SDL_GL_GetProcAddress("glFramebufferTexture2DEXT");
	glFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)
		SDL_GL_GetProcAddress("glFramebufferRenderbufferEXT");
	glGenerateMipmapEXT = (PFNGLGENERATEMIPMAPEXTPROC)
		SDL_GL_GetProcAddress("glGenerateMipmapEXT");
	glGenRenderbuffersEXT = (PFNGLGENRENDERBUFFERSEXTPROC)
		SDL_GL_GetProcAddress("glGenRenderbuffersEXT");
	glGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC)
		SDL_GL_GetProcAddress("glGenFramebuffersEXT");
	glDeleteRenderbuffersEXT = (PFNGLDELETERENDERBUFFERSEXTPROC)
		SDL_GL_GetProcAddress("glDeleteRenderbuffersEXT");
	glDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC)
		SDL_GL_GetProcAddress("glDeleteFramebuffersEXT");
	
	// VBO Related
	glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)
		SDL_GL_GetProcAddress("glGenBuffersARB");
	glBindBufferARB = (PFNGLBINDBUFFERARBPROC)
		SDL_GL_GetProcAddress("glBindBufferARB");
	glBufferDataARB = (PFNGLBUFFERDATAARBPROC)
		SDL_GL_GetProcAddress("glBufferDataARB");
	glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)
		SDL_GL_GetProcAddress("glDeleteBuffersARB");
	glVertexAttribPointerARB = (PFNGLVERTEXATTRIBPOINTERARBPROC)
		SDL_GL_GetProcAddress("glVertexAttribPointerARB");
	
	
	// GLSL Related
	glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC)
		SDL_GL_GetProcAddress("glCreateShaderObjectARB");
	glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC)
		SDL_GL_GetProcAddress("glShaderSourceARB");
	glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC)
		SDL_GL_GetProcAddress("glCompileShaderARB");
	glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC)
		SDL_GL_GetProcAddress("glGetObjectParameterivARB");
	glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC)
		SDL_GL_GetProcAddress("glGetInfoLogARB");
	glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC)
		SDL_GL_GetProcAddress("glCreateProgramObjectARB");
	glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC)
		SDL_GL_GetProcAddress("glAttachObjectARB");
	glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC)
		SDL_GL_GetProcAddress("glLinkProgramARB");
	glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC)
		SDL_GL_GetProcAddress("glUseProgramObjectARB");
	glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC)
		SDL_GL_GetProcAddress("glGetUniformLocationARB");
	glGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC)
		SDL_GL_GetProcAddress("glGetAttribLocationARB");
	glUniform2fARB			= (PFNGLUNIFORM2FARBPROC)
		SDL_GL_GetProcAddress("glUniform2fARB");
	glUniform3fARB			= (PFNGLUNIFORM3FARBPROC)
		SDL_GL_GetProcAddress("glUniform3fARB");
	glUniform1iARB			= (PFNGLUNIFORM1IARBPROC)
		SDL_GL_GetProcAddress("glUniform1iARB");
	glEnableVertexAttribArrayARB = (PFNGLENABLEVERTEXATTRIBARRAYARBPROC)
		SDL_GL_GetProcAddress("glEnableVertexAttribArrayARB");
	glEnableVertexAttribArrayARB = (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) 
		SDL_GL_GetProcAddress("glDisableVertexAttribArrayARB");

#ifndef __GNUC__
	glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC)
			SDL_GL_GetProcAddress("glActiveTextureARB");
	glClientActiveTexture = (PFNGLCLIENTACTIVETEXTUREPROC)
		SDL_GL_GetProcAddress("glClientActiveTexture");
#endif
};

Renderer::~Renderer()
{
	delete mpRoot;
};

void Renderer::RenderAll() {
	mpRoot->renderOneFrame();
};

void Renderer::Render(const WtfEngine::IScreen::tRef &rRenderTarget, const WtfEngine::IPerspective::tRef &rCamera, tRenderType type)
{
	switch(type) {
		case RENDER_WORLD_WIREFRAME:
			// TODO: enable wireframe
		case RENDER_WORLD:
			// TODO: culling
			RenderAll();
			break;

		case RENDER_PHYSICS:
			PhysicsEngine::GetInstance().getWorld()->debugDrawWorld();
			break;
	};
};


/**
 * Renders a scene. Clears the screen, set ups the viewport, activates
 * various settings. Then renders the Render List first (3Ds) , followed by 
 * the Overlay Stack (HUD, Developer's Console etc...)
 */
void Renderer::Run()
{
	// TODO: update all positions

	RenderAll();
};

